#include <cstring>
#include <misc_utils.h>
#include <chord.h>
#include <id_utils.h>
#include "rpclib.h"
#include <dhash_common.h>
#include <dhblock.h>

#include "incognito_proto.h"
#include "incognitoblockingclient.hh"

u_int64_t starttime;

IncognitoBlockingClient::IncognitoBlockingClient(chord_node node_arg): cnode(node_arg)
{
	warn << "IncognitoBlockingClient(chord_node node_arg): "<<this->cnode.x<<" "<<this->cnode.r.hostname<<" "<<this->cnode.r.port<<"\n";
}

bigint IncognitoBlockingClient::insert(str data) throw(IncognitoInsertException)
{
	bigint key = compute_hash(data.cstr(),data.len());
	return insert(key, data);
}
void IncognitoBlockingClient::insert_cb(ptr<incognito_store_arg> sa, clnt_stat err)
{
	warnx << "IncognitoBlockingClient::insert_cb() - start\n";
	if (err)
	{
		fatal << "IncognitoBlockingClient::insert: RPC failed: " << err << "\n";
		return;
	}
	cb_ok=true;

	warnx << "IncognitoBlockingClient::insert - RPC CALLBACK!!\n";
	warnx << "IncognitoBlockingClient::insert - err: <" << err << ">\n";
	warnx << "cnode.x=" << cnode.x << "\n";
	warnx << "time: " << (getusec () -starttime)/1000 << " msec\n";
	write_ok = true;
	warnx << "IncognitoBlockingClient::insert_cb() - stop\n";
}

bigint IncognitoBlockingClient::insert(bigint key, str data, bool key_is_hash) throw(IncognitoInsertException){
	warnx << "IncognitoBlockingClient::insert() - start\n";
	dhash_ctype c = key_is_hash?DHASH_CONTENTHASH:DHASH_NOAUTH;

	warnx << "data.len()=" << data.len() << "\n";
	ptr<incognito_store_arg> sa = New refcounted<incognito_store_arg> ();
	sa->data.setsize(data.len());
	memcpy(sa->data.base(),data.cstr(),data.len());
	sa->key_type = c;
	sa->key_value = key;
	warnx << "IncognitoBlockingClient::insert: sa->key_value =" << sa->key_value  << "\n";
	starttime = getusec ();
	write_ok = false;
	cb_ok=false;
	doRPC (cnode, incognito_program_1, INCOGNITOPROC_STORE, sa, NULL, wrap (mkref(this), &IncognitoBlockingClient::insert_cb, sa));
	
	wait_for_cb();

	if (!write_ok)
		throw new IncognitoInsertException();
	warnx << "IncognitoBlockingClient::insert() - stop\n";
	return key;
}
/*
str IncognitoBlockingClient::retrieve(bigint key, bool key_is_hash) throw(IncognitoRetrieveException) {
	dhash_ctype c = key_is_hash?DHASH_CONTENTHASH:DHASH_NOAUTH;

	read_ok=false;
	str tmp;
	
//	m_client->retrieve(key,c,wrap(this, &IncognitoBlockingClient::retrieve_cb, &tmp));

	wait_for_cb();

	if (!read_ok)
		throw new DhashRetrieveException();
	return tmp;
}

void IncognitoBlockingClient::retrieve_cb(str *buf, dhash_stat stat, ptr<dhash_block> bl, vec<chordID> vc) {
	cb_ok=true;
	warn << "IncognitoBlockingClient::retrieve_cb stat= " << stat << "\n";
	if(stat != DHASH_OK)
		return;
//	warn << "IncognitoBlockingClient::retrieve_cb bl->data: >" << bl->data << "< " << bl->data.len() << "\n";
//	warn << "IncognitoBlockingClient::retrieve_cb bl->vData.size(): " << bl->vData.size() << "\n";

	if (bl->vData.size()==0) {
		*buf=bl->data;
	} else {
//		for (size_t i=0; i< bl->vData.size(); ++i) {
//			warn << "\tLOOP: vData[" << i << "]= >" << bl->vData[i] << "<\n";
//		}
		*buf = bl->vData[bl->vData.size()-1];
	        }

	read_ok=true;
	return;
}
*/
void IncognitoBlockingClient::retrieve_cb(ptr<incognito_fetch_arg> arg, incognito_fetch_res *res, clnt_stat err)
{
	cb_ok=true;
	warnx << "IncognitoBlockingClient::retrieve_cb stat= " << err << "\n";
	//FIXME
	warnx << "IncognitoBlockingClient::retrieve_cb(): - arg->key_type=" << arg->key_type << "\n";
	warnx << "IncognitoBlockingClient::retrieve_cb(): - arg->key_value=" << arg->key_value << "\n";
	warnx << "IncognitoBlockingClient::retrieve_cb(): - res->data=" << str(res->data.base(),res->data.size())  << "\n";
	warnx << "cnode.x=" << this->cnode.x << "\n";
	warnx << "time: " << (getusec () -starttime)/1000 << " msec\n";
	read_ok=true;
	return;
//	read_ok=false;

}
str IncognitoBlockingClient::retrieve(bigint key, bool key_is_hash) throw(IncognitoRetrieveException)
{
	dhash_ctype c = key_is_hash?DHASH_CONTENTHASH:DHASH_NOAUTH;

	str tmp;
	warnx << "IncognitoBlockingClient::retrieve() " << __FILE__ << ":" << __LINE__ << "\n";
	warnx << "IncognitoBlockingClient::retrieve(): - key_type=" << c << "\n";
	warnx << "IncognitoBlockingClient::retrieve(): - key_value=" << key << "\n";

	
        incognito_fetch_res *res=New incognito_fetch_res;
        ptr<incognito_fetch_arg> arg = New refcounted<incognito_fetch_arg> ();
	arg->key_type = c;
	arg->key_value = key;
	starttime = getusec ();
	cb_ok=false;
	read_ok=false;
	doRPC (cnode, incognito_program_1,
			INCOGNITOPROC_FETCH, arg, res, wrap (mkref(this), &IncognitoBlockingClient::retrieve_cb, arg, res));

	wait_for_cb();

	if (!read_ok)
		throw new IncognitoRetrieveException();
	return str(res->data.base(),res->data.size());
}

/*
 *
 void test_fetch_cb(ptr<incognito_fetch_arg> arg, incognito_fetch_res *res, clnt_stat err);
 void test_fetch(bigint id)
 {
	 incognito_fetch_res *res=New incognito_fetch_res;
	 ptr<incognito_fetch_arg> arg = New refcounted<incognito_fetch_arg> ();
	 arg->key_type = DHASH_CONTENTHASH;
	 arg->key_value = id;
	 starttime = getusec ();
	 doRPC (dst, incognito_program_1,
	 INCOGNITOPROC_FETCH, arg, res, wrap (&test_fetch_cb, arg, res));
 }

 void test_fetch_cb(ptr<incognito_fetch_arg> arg, incognito_fetch_res *res, clnt_stat err)
 {
 	warnx << "test_fetch_cb(): - err=" << err << "\n";
	 warnx << "test_fetch_cb(): - arg->key_type=" << arg->key_type << "\n";
	 warnx << "test_fetch_cb(): - arg->key_value=" << arg->key_value << "\n";
	 warnx << "test_fetch_cb(): - res->data=" << str(res->data.base(),res->data.size())  << "\n";
	 warnx << "dst.x=" << dst.x << "\n";
	 warnx << "time: " << (getusec () -starttime)/1000 << " msec\n";
	 exit(0);
 }
 *
 *
 * */
void IncognitoBlockingClient::wait_for_cb() {

	while (!cb_ok) {
		warn << "wait_for_cb - iter()\n";
		acheck();
	}
}

